package org.xx.armory.swing.controllers;

import org.xx.armory.swing.MDIFrameUIController;
import org.xx.armory.swing.builders.UIModel;
import org.xx.armory.swing.components.InternalFramePane;
import org.xx.armory.swing.components.StatusBar;

import javax.swing.*;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Image;
import java.beans.PropertyVetoException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

import static org.xx.armory.commons.Validators.notBlank;
import static org.xx.armory.commons.Validators.notNull;
import static org.xx.armory.swing.UIUtils.assertUIThread;

public class MDIFrameUIModelController
        extends FrameUIModelController
        implements MDIFrameUIController {
    private JDesktopPane desktop;
    private StatusBar statusBar;
    private Map<String, InternalFramePane> children;

    public MDIFrameUIModelController(
            JFrame frame
    ) {
        super(frame);

        frame.setLayout(new BorderLayout());
        createDesktop();

        this.children = new LinkedHashMap<>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void loadFromUIModel(
            UIModel uiModel
    ) {
        super.loadFromUIModel(uiModel);
    }

    private void createDesktop() {
        final JDesktopPane desktop = new JDesktopPane();
        desktop.setOpaque(false);
        desktop.setBackground(Color.DARK_GRAY);
        desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
        desktop.setDesktopManager(new DefaultDesktopManager() {
        });
        this.getContainer().add(desktop, BorderLayout.CENTER);
        this.desktop = desktop;

        final StatusBar statusBar = new StatusBar();
        this.getContainer().add(statusBar, BorderLayout.SOUTH);
        this.statusBar = statusBar;
        // this.getContainer().setContentPane(desktop);

        final JDesktopPane desktopModal = new JDesktopPane();
        desktopModal.setOpaque(false);
        desktopModal.setBackground(new Color(0x77000000));
        desktopModal.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
        desktopModal.setDesktopManager(new DefaultDesktopManager() {
            @Override
            public void openFrame(
                    JInternalFrame f
            ) {
                super.openFrame(f);
                f.getDesktopPane().setVisible(true);
            }

            @Override
            public void closeFrame(
                    JInternalFrame f
            ) {
                super.closeFrame(f);
                f.getDesktopPane().setVisible(false);
            }
        });
        desktopModal.setVisible(false);
        this.getContainer().setGlassPane(desktopModal);
    }

    public JDesktopPane getDesktop() {
        return this.desktop;
    }

    @Override
    public void addChild(
            JInternalFrame child
    ) {
        assertUIThread();

        notNull(child, "child");

        getDesktop().add(child, JDesktopPane.DEFAULT_LAYER);

        activateChild(child);
    }

    @Override
    public void openChild(
            String name,
            Supplier<InternalFramePane> childSupplier
    ) {
        assertUIThread();

        name = notBlank(name, "name").trim();
        notNull(childSupplier, "childSupplier");

        synchronized (this) {
            final InternalFramePane child = this.children.get(name);
            if (child == null) {
                final InternalFramePane newChild = childSupplier.get();
                if (newChild != null) {
                    final String childName = name;
                    this.children.put(childName, newChild);

                    final JInternalFrame frame = new JInternalFrame("", true, true, true, false);
                    newChild.setOwner(frame);
                    frame.setContentPane(newChild);

                    Image iconImage = newChild.getIcon();
                    if (iconImage == null) {
                        iconImage = getContainer().getIconImages().stream().findAny().orElse(null);
                    }
                    frame.setFrameIcon(iconImage != null ? new ImageIcon(iconImage) : null);
                    frame.setTitle(newChild.getTitle());
                    frame.pack();
                    if (newChild.getDefaultButton() != null) {
                        frame.getRootPane().setDefaultButton(newChild.getDefaultButton());
                    }

                    frame.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
                    frame.addInternalFrameListener(new InternalFrameAdapter() {
                        @Override
                        public void internalFrameClosed(
                                InternalFrameEvent event
                        ) {
                            super.internalFrameClosed(event);

                            MDIFrameUIModelController.this.children.remove(childName);
                        }
                    });
                    getDesktop().add(frame, JDesktopPane.DEFAULT_LAYER);

                    activateChild(frame);
                }
            } else {
                final JInternalFrame frame = child.getOwner();
                if (frame == null) {
                    throw new IllegalStateException("internal owner is null");
                }

                activateChild(frame);
            }
        }
    }

    @Override
    public InternalFramePane getSelectedChild() {
        final JInternalFrame selectedFrame = this.desktop.getSelectedFrame();

        if (selectedFrame == null) {
            return null;
        }

        return this.children.values().stream()
                            .filter(Objects::nonNull)
                            .filter(child -> selectedFrame.equals(child.getOwner()))
                            .findAny()
                            .orElse(null);
    }

    public StatusBar getStatusBar() {
        return this.statusBar;
    }

    protected void activateChild(
            JInternalFrame frame
    ) {
        notNull(frame, "frame");

        if (frame.isIcon()) {
            try {
                frame.setIcon(false);
            } catch (PropertyVetoException ex) {
                // 忽略此异常。
            }
        }

        frame.setVisible(true);
        try {
            frame.setSelected(true);
        } catch (java.beans.PropertyVetoException ex) {
            // 忽略异常。
        }
        frame.toFront();
        try {
            frame.setMaximum(true);
        } catch (java.beans.PropertyVetoException ex) {
            // 忽略异常。
        }
    }
}
